
<template>
  <section
    v-if="show"
    class="h-transfer single-tab">
    <div class="mask"></div>
    <!-- 内容面板 -->
    <div class="transfer__content">
      <!-- 面板顶部标题 -->
      <header class="transfer__header">
        <i class="el-icon-monitor"></i>
        {{ title }}
      </header>
      <!-- 穿梭框主要内容 -->
      <div class="transfer__body">

        <el-tabs>

          <el-tab-pane label="待选择" v-if="enableEdit">
            <!-- 左边穿梭框 -->
            <div class="transfer-pane" >
              <!-- 操作栏 -->
              <!-- <div class="transfer-pane__tools">

              </div> -->
              <!-- 穿梭框 -->
              <div class="transfer-pane__body" style="">
                <div class="enough-mask" v-show="isEnough">
                  <span class="p-center">最多选择{{ maxNum }}项</span>
                </div>
                <div
                        class="searchResPane"
                        :class="{ active: searchMode }"
                        v-loading="searchLoading" >
                  <div class="hidden-tag" @click="searchString = ''">关闭</div>
                  <div v-for="(item, index) in searchRes" :key="index" class="item">
                    <div>
                      <div>{{ getNodeProp(item, 'label') }}</div>
                      <div class="text-ellipsis search-res-tip" >
                        {{ getNodeProp(item, 'searchResTip') }}
                      </div>
                    </div>
                    <el-checkbox @change="checked => checked ? addData(item) : removeData(item, true)"></el-checkbox>
                  </div>

                  <div v-if="searchRes.length <= 0" class="item" style="text-align: center;display:block;">
                    无匹配项
                  </div>

                </div>

                <el-input
                        v-model="searchString"
                        class="search-input"
                        size="mini"
                        style="width: 95%;height: 40px;"
                        type="search"
                        placeholder="搜索"
                        :disabled="!searchable"
                        prefix-icon="el-icon-search"
                ></el-input>

                <el-divider content-position="right" style="margin: 12px 0px;"></el-divider>

                <el-scrollbar style="height:80vh;">

                  <el-tree
                          :ref="config.type"
                          :lazy="true"
                          show-checkbox
                          :props="{
                            children: config.children,
                            label: config.label,
                            isLeaf: config.isLeaf,
                            disabled: config.disabled
                          }"
                          :default-expanded-keys="['1']"
                          :load="onLoad"
                          node-key="nodeId"
                          :check-strictly="true"
                          @check-change="(data, checked) => onCheckChange(data, checked)"

                          style="height: 100vh;"
                  >
                  </el-tree>

                </el-scrollbar>
              </div>
            </div>
          </el-tab-pane>

          <el-tab-pane :label="'已选择(' + (aloneCheckedData.length + selectedData.length) + ')'">
            <!-- 右边穿梭框 -->
            <div class="transfer-pane">
              <el-scrollbar class="transfer-pane__body shadow right-pane">

                <div
                        v-for="(item, index) in selectedData"
                        :key="index"
                        class="selected-item"
                >
                  <el-avatar style="background-color: #3296fa">{{getNodeProp(item, 'label').length > 2 ? getNodeProp(item, 'label').substring(1) : getNodeProp(item, 'label')}}</el-avatar>
                  <label>{{ getNodeProp(item, 'label') }}</label>

                  <i v-if="enableEdit"
                          class="el-icon-delete"
                          @click="removeData(item)"
                  ></i>
                </div>

                <div
                        v-for="(item, index) in aloneCheckedData"
                        :key="'alone'+ index"
                        class="selected-item"
                >

                  <el-avatar style="background-color: #3296fa">{{getNodeProp(item, 'label').length > 2 ? getNodeProp(item, 'label').substring(1) : getNodeProp(item, 'label')}}</el-avatar>
                  <label>{{ getNodeProp(item, 'label') }}</label>

                  <i v-if="enableEdit"
                            class="el-icon-delete"
                            @click="removeData(item, true)"
                  ></i>
                </div>

              </el-scrollbar>
            </div>
          </el-tab-pane>

        </el-tabs>



      </div>

      <footer class="transfer__footer" style="text-align: center;border-top: 1px solid #eee;">
        <el-button size="small" @click="closeTransfer">返回</el-button>
        <el-button size="small" type="primary" @click="confirm" v-if="enableEdit">确定</el-button>
      </footer>

    </div>
  </section>
</template>

<script>
/* eslint-disable no-prototype-builtins */
/* eslint-disable no-debugger */
/* eslint-disable no-return-assign */
/* eslint-disable handle-callback-err */

import { CONFIG_LIST } from './config.js'
import { debounce } from '@/assets/utils/index.js'

export default {
  name: 'FlowOrgSelect',

  model:{
    prop: 'value',
    event: 'input'
  },
  props: {
    // v-model 已经选择过的数据 用于回显
    value: {
      type: Array,
      default: () => ([])
    },
    // 字符串数组使用配置文件的预设值
    // 对象数组需要包含 key/conf 两个属性 conf 会覆盖对应key的默认配置
    type: {
      type: String,
      default: () => 'dep'
    },
    title: {
      type: String,
      default: '组织机构'
    },
    show: {
      type: Boolean,
      reuired: true
    },
    // 是否支持模糊查询
    searchable: {
      type: Boolean,
      default: true
    },
    // 可选择的最大数量
    maxNum: {
      type: Number,
      default: 99
    },

    /**
     * 是否允许编辑
     */
    enableEdit: {
      type: Boolean,
      reuired: false
    },
    filterConfig: {
      type: Object,
      default: {}
    }
  },
  data () {

    return {
      searchRes: [],  // 搜索后的结果
      selectedData: [],   // 用户手动选择的节点(在tree里面已经显示的节点)
      aloneCheckedData: [], // 已有的 但是未在tree中渲染的数据 例如回显时的数据
      isEnough: false,  // 是否选择了足够的人数
      searchString: '',
      searchMode: false,  // 是否展示搜索面板
      searchLoading: false
    }
  },
  computed: {
    selectedNum () {
      return this.selectedData.length;
    }
  },
  mounted () {
    this.isNumEnough()
    this.debounceSearch = debounce(this.search, 500)
  },
  methods: {
    onLoad (node, resolve) {

      var that = this;

      this.config.onload(node, that.filterConfig).then(res => {
        const nodes = res.map( t => ( { nodeId: that.config.nodeId(t), ...t } ) )
        resolve(nodes)
      })
      .then(res=>{

        const tree = that.$refs[that.type];

        that.aloneCheckedData.forEach(data => {
          tree.setChecked(data.nodeId, true)
        })
      })
    },

    search () {

      var that = this;

      if (!that.searchString) {

        that.searchRes = []
        return
      }

      that.searchLoading = true

      new Promise((resolve, reject) => {

        that.config.onsearch(that.searchString, that.filterConfig, resolve, reject)

      }).then(res => {

        console.info(res);

        that.searchRes = res.map(t => ({ nodeId: that.config.nodeId(t), ...t }))

        console.info(that.searchRes);
      })
      .catch(err => console.warn(err))
      .finally(() => that.searchLoading = false)
    },

    onCheckChange (data, checked) {

      var that = this;

      const index = that.aloneCheckedData.findIndex(t => t.nodeId === data.nodeId)

      if (index > -1) {
        that.aloneCheckedData.splice(index, 1)
      }

      that.$nextTick(() => {

        const tree = that.$refs[that.type]

        const nodes = tree.getCheckedNodes().map(t => {
          !t.nodeId && (t.nodeId = that.getNodeProp(t, 'nodeId'))
          return t
        })

        that.selectedData = nodes;

        that.isNumEnough()
        that.$forceUpdate()
      })
    },

    addData (data) {

      const tree = this.$refs[this.type]

      tree.setChecked(data.nodeId, true)
      !tree.getCheckedKeys(data).includes(data.nodeId)
      && !this.aloneCheckedData.find(t => t.nodeId === data.nodeId)
      && this.aloneCheckedData.push(data)
    },

    removeData (data, fromAloneData = false) {

      if (fromAloneData) {

        const index = this.aloneCheckedData.findIndex(t => t.nodeId === data.nodeId)
        index > -1 && this.aloneCheckedData.splice(index, 1)

      } else {

        //取消树节点选中状态
        this.$refs[this.type].setChecked(data.nodeId, false)  // 触发onCheckChange
      }
    },

    removeAll () {

      const tree = this.$refs[this.type]

      tree.getCheckedKeys().forEach(function(key){
        tree.setCheckedKeys([])
      })

      this.selectedData = []
      this.aloneCheckedData = []
    },

    isNumEnough () {

      let count = 0
      count += this.selectedData.length
      count += this.aloneCheckedData.length

      this.isEnough = (count >= this.maxNum ? true : false)
    },

    closeTransfer () {

      this.$emit('update:show', false)

      this.isEnough = false
      this.searchString = ''
    },

    confirm () {

      var that = this;

      const tree = that.$refs[that.type]

      const nodes = tree.getCheckedNodes().map(t => {
        !t.nodeId && (t.nodeId = that.getNodeProp(t, 'nodeId'))
        return t
      })

      var nodesMap = {};

      for(var i = 0; i < nodes.length; i ++){
        nodesMap[nodes[i].nodeId] = nodes[i];
      }

      for(var i = 0; i < this.aloneCheckedData.length; i ++){

        if(!nodesMap[this.aloneCheckedData[i].nodeId]){
          nodes.push(this.aloneCheckedData[i]);
        }
      }

      this.$emit('input', JSON.parse(JSON.stringify(nodes)))

      try{

        this.$emit('onConfirm', JSON.parse(JSON.stringify(nodes)))

      }catch (e) {

      }

      this.closeTransfer()
    },

    getConfProp(propName){

      return this.config ? this.config[propName] : null
    },

    getNodeProp(data, propName){
      try{

        console.info("data:" + JSON.stringify(data))
        console.info("propName:" + propName)

        const prop = this.getConfProp(propName)

        console.info("prop:" + JSON.stringify(prop))

        if(typeof prop === 'string'){
          return data[prop]
        }
        if(typeof prop === 'function'){
          return prop(data)
        }
      }catch(e){
        console.error(e)
        return '执行出错，可联系开发人员'
      }
    },

    dataInit(){

      var that = this;

      this.aloneCheckedData = []
      this.selectedData = []
      this.config = {}


        CONFIG_LIST.find(function(cfg){

          if(cfg.type == that.type){

            that.config = cfg;
          }
        })

      if(that.value && that.value.length > 0){

        that.aloneCheckedData = JSON.parse(JSON.stringify(that.value))

        for(var i = 0; i < that.aloneCheckedData.length; i++){
          that.aloneCheckedData[i].nodeId = that.config.nodeId(that.aloneCheckedData[i]);
        }
      }
    }
  },
  watch: {
    searchString (newVal) {
      this.searchMode = !!newVal
      this.debounceSearch()
    },

    show: {
      handler: function (show) {
        if (show) {
          this.dataInit()
          this.isNumEnough()
        }
      },
      immediate: true
    },

    type:{
      handler: function(val){
        this.dataInit() // type 比show 延后
      },
      immediate: true,
      deep: true
    },

    enableEdit:{
      handler: function(val){
        this.enableEdit = val;
      },
      immediate: true,
      deep: true
    },

    filterConfig:{
      handler: function(val){

        this.filterConfig = val;
      },
      immediate: true,
      deep: true
    },

    maxNum:{
      handler: function(val){

        this.maxNum = val;
      },
      immediate: true,
      deep: true
    },


  }
}
</script>
<style lang="stylus">

  .h-transfer{
    text-align: left;
    position: fixed;
    width: 100vw;
    height: 100vh;
    top: 0;
    left: 0;
    z-index: 2999;
    line-height: 32px;

    > .mask {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .el-tabs--border-card {
      box-shadow: none;
    }

    .el-tabs__content {
      overflow: visible;
      min-height: 250px;
    }

    .el-tabs__nav {
      width: 100%;
      display: flex;

      > .el-tabs__item {
        flex-grow: 1;
      }
    }

    &.single-tab .el-tabs__item {
      text-align: center;
      background: #f5f7fa !important;
      border-bottom: 1px solid #e4e7ed;
      border-right-width: 0px;
    }

    .el-tree-node__content > label.el-checkbox {
      // position: absolute;
      // right: 0;
    }

    .searchResPane{
      position: absolute;
      overflow-y: auto;
      z-index: 99;
      left: 0;
      top: 100%;
      width: 100%;
      height: 100%;
      background: white;
      border: 1px solid #dcdfe6;
      transition: top .5s;
      margin-top: 50px;



      &.active{
          top: 0;
      }

      .hidden-tag{
          color:#999;
          font-size:12px;
          text-align:right;
          padding-top:4px;
          padding-right:12px;
          cursor pointer

          &:hover{
              color: #66b1ff;
          }
      }

      .item{
          padding: 4px 14px;
          display: flex;
          justify-content: space-between;
          align-items: center;
          line-height 1.5
          &:hover{
              background-color: #ecf5ff;
              color: #66b1ff;
              cursor: pointer;
          }

          .search-res-tip{
            font-size:12px;
            color:#999;
            max-width: 280px;
          }
      }
    }

    .enough-mask{
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      color: white;
      font-size: 16px;
      z-index: 100;
      height: 100%;
      background: rgba(0,0,0,0.5);
      letter-spacing: 4px;
    }

    .p-center{
        position absolute
        top 50%
        left 50%
        transform translate(-50%, -50%)
    }

    .transfer__content {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 800px;
    height: 600px;
    background: white;
    overflow: hidden;
    border-radius: 4px;


    .el-divider--horizontal {
      display: block;
      height: 1px;
      width: 100%;
      margin-right: 0px;
      margin-left: 0px;
      margin-top: 12px;
      margin-bottom: 12px;
    }

    .el-input__inner{
      height: 40px;
      line-height: 40px;
    }
  }

  .transfer__header {
    margin-bottom: 6px;
    background: #409EFF;
    padding: 6px 24px;
    color: white;
    .el-icon-close{
      cursor:pointer;
      float: right;
      margin-top: 10px;
    }
  }


  .transfer__body {
    padding: 12px 0;
    display: flex;
    justify-content: space-around;
  }

  .transfer-pane {
    width: 360px;
  }
    .search-input  input{
      border: 1px solid #DCDFE6 !important;
      &:focus{
        border-color:#409EFF !important;
      }
    }

  .transfer-pane__tools {
    margin-bottom: 0px!important;
    height: 40px!important;
    display: block!important;
    justify-content: space-between;
    align-items: center;
    text-align: right;
    padding-right: 13px;



    span:last-child {
      cursor: pointer;
    }
  }

  .transfer-pane__body {
    position relative
    width: 100%;
    height: 330px;
    overflow hidden
    font-size: 14px;

    >>> .el-scrollbar__view{
      height: 100%;
    }

    .el-tabs__item {
      height: 26px;
      line-height: 26px;
    }

    .el-scrollbar__view {
      overflow-x: visible!important;
    }


    .is-disabled
    {
      display: none;
    }
  }

  .transfer-icons {
    display: flex;
    flex-direction: column;
    justify-content: center;

    i {
      margin-top: 20px;
      margin-bottom: 20px;
      cursor: pointer;
      font-size: 20px;
      color: #696969;

      &:hover {
        transform: scale(1.1);
      }
    }
  }

  .tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 8px;

    .node-label {
      max-width: 250px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      display: inline-block;
    }

    .node-checkbox {
      position: absolute;
      right: 0;
    }

    i {
      &:hover {
        color: #1485f8;
        cursor: pointer;
      }

      font-size: 10px;
    }

  }

  .right-pane {
    box-sizing: border-box;
    overflow-x: hidden;
    height: 85vh!important;
    margin-bottom: 10px;

    .selected-item {
      padding: 0px 12px;
      display: flex;
      justify-content: space-between;
      align-items: center;

      &:hover {
        background-color: #F5F7FA;
      }

      span {
        max-width: 40px!important;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }

      i {
        &:hover {
          color: #1485f8;
          cursor: pointer;
        }
      }
    }
  }

  .dot{
    width: 2px;
    height: 2px;
    display: inline-block;
    border-radius: 50%;
    background: #4caf50;
  }

    .text-ellipsis{
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }

.single-tab{

  .transfer__header {
    margin-bottom: 0px!important;
    background: #409EFF!important;
    padding: 6px 24px;
    padding-bottom: 0px!important;
    color: white;
    text-align: center;
    .el-icon-close{
      cursor:pointer;
      float: right;
      margin-top: 10px;
    }
  }

  .transfer__body{
    padding-top: 0px!important;
  }

  .transfer__content{
    left: none!important;
    right: none!important;
    height: 100%!important;
    width: 100%!important;
  }

  .transfer-pane{
    width: 100vw!important;
    padding-left: 5vw;
    padding-right: 5vw;
  }

  .el-divider--horizontal{
    width: 95%!important;
  }

  .transfer__footer{
    position: absolute;
    bottom: 0;
    left: 0;
    padding: 6px 12px;
    width: 100%;
    box-sizing: border-box;
    text-align: right;
  }

  .el-tabs__content{
    height: 100vh;
  }

  .transfer-pane__body{
    height: 100vh;
  }

  .selected-item{
    border-bottom: 1px solid rgb(238, 238, 238);
    height: 60px;

    label{
      position absolute;
      left 60px;
    }
  }

  .searchResPane{
    height 80vh!important;
    width: 95%!important;
  }
  .enough-mask{
    height: 80%;
  }

}
</style>
